import babelTraverse from '@babel/traverse';
import * as babelParser from '@babel/parser';
import * as vueTemplateCompiler from 'vue-template-compiler';
import { isObjectExpression, isObjectProperty, isArrayExpression, isStringLiteral, isObjectMethod, isExportDefaultDeclaration, isIdentifier } from '@babel/types';

import { readFileContent, isNotEmptyObject } from '../../utils';
import { IVueSfcParseResult, ITemplate, ISlots, IScript, IProps, IProp, IMethods, IModel } from '../../types/vue-sfc';
import { parseCommentForPublicMark, parseCommentForEventMark, parseCommentForVueEvent, parseCommentBlock, isCommentBlock } from '../../comment';

import {
    getObjectPropName,
    getObjectPropValue,
    getObjectPropertyValue,
    getMethodName,
    getEmitEventInfo,
    isEmitEvent,
    findSlotNodes,
    getSlotNodeDesp,
    combineEmitEventInfo,
    getSlotName
} from './utils';

export default parseComponent;

/**
 * 解析组件
 */
function parseComponent(filepath: string) {
    const code = readFileContent(filepath);
    if (code) {
        // @ts-ignore
        const parseResult: IVueSfcParseResult = {};
        const component = vueTemplateCompiler.parseComponent(code);

        if (component.template) {
            const template = parseTemplate(component.template);
            if (template) parseResult.template = template;
        }

        if (component.script) {
            const script = parseScript(component.script);
            if (script) parseResult.script = script;
        }

        return parseResult as IVueSfcParseResult;
    }
}

/**
 * 解析组件 template
 */
function parseTemplate(template: vueTemplateCompiler.SFCBlock) {
    const templateAst = vueTemplateCompiler.compile(template.content, {
        // @ts-ignore
        comments: true,
        whitespace: 'condense'
    }).ast;

    if (templateAst) {
        const parseResult: ITemplate = {};

        // ...
        // 解析 Slot
        const slotNodes = findSlotNodes(templateAst);
        if (slotNodes.length) {
            const slots: ISlots = {};
            for (const slot of slotNodes) {
                const slotName = getSlotName(slot);
                slots[slotName] = {};
                const desp = getSlotNodeDesp(slot);
                if (desp) slots[slotName].desp = desp;
                if (slot.attrs && slot.attrs.length) slots[slotName].attrs = slot.attrs;
            }
            if (isNotEmptyObject(slots)) parseResult.slots = slots;
        }

        if (isNotEmptyObject(parseResult)) return parseResult;
    }
}

/**
 * 解析组件 script
 */
function parseScript(script: vueTemplateCompiler.SFCBlock) {
    const parseResult: IScript = {
        model: {
            prop: {
                value: 'value'
            },
            event: {
                value: 'input'
            }
        }
    };

    const scriptAst = babelParser.parse(script.content, {
        sourceType: 'module'
    });

    babelTraverse(scriptAst, {
        ExportDefaultDeclaration(nodePath) {
            let exportDefaultNode = null;
            if (Array.isArray(nodePath.container)) {
                exportDefaultNode = nodePath.container.find(f => {
                    return isExportDefaultDeclaration(f);
                });
            } else if (isExportDefaultDeclaration(nodePath.container)) {
                exportDefaultNode = nodePath.container;
            }

            if (!isExportDefaultDeclaration(exportDefaultNode)) return;

            // Vue 选项
            // @ts-ignore
            const options = exportDefaultNode.declaration.properties;

            for (const option of options) {
                const optionName = option.key.name;
                const optionValue = option.value;

                switch (optionName) {
                    /**
                     * Name 选项 - string
                     *
                     * https://cn.vuejs.org/v2/api/#name
                     */
                    case 'name': {
                        if (isStringLiteral(optionValue)) {
                            parseResult.name = optionValue.value;
                        }
                        break;
                    }

                    /**
                     * Props 选项 - Array<string> | Object
                     *
                     * https://cn.vuejs.org/v2/api/#props
                     */
                    case 'props': {
                        const props: IProps = {};

                        if (isObjectExpression(optionValue)) {
                            for (const prop of optionValue.properties) {
                                if (isObjectProperty(prop)) {
                                    // ...
                                    // 解析 Prop 对象
                                    const propName = getObjectPropName(prop);
                                    if (propName) {
                                        const map: IProp = {};

                                        // 解析 Prop 对象中的属性
                                        if (isObjectExpression(prop.value)) {
                                            for (const item of prop.value.properties) {
                                                const name = getObjectPropName(item);
                                                if (name) {
                                                    const valueInfo = getObjectPropValue(item, script.content, { formatCode: true });
                                                    if (name === 'type') {
                                                        if (valueInfo.type === 'binary') {
                                                            map.type = (valueInfo.value as string).split('|');
                                                        } else {
                                                            map.type = [valueInfo.value as string];
                                                        }
                                                    } else if (name === 'default') {
                                                        map.default = valueInfo.value;
                                                        // @ts-ignore
                                                        map.defaultValueType = valueInfo.type;
                                                    } else {
                                                        // @ts-ignore
                                                        map[name] = valueInfo.value;
                                                    }
                                                }
                                            }
                                        }

                                        // 解析 Prop 注释
                                        if (prop.leadingComments) {
                                            const lastComment = prop.leadingComments[prop.leadingComments.length - 1];
                                            if (isCommentBlock(lastComment)) {
                                                const info = parseCommentBlock(lastComment);
                                                if (info) Object.assign(map, info);
                                            } else {
                                                const desp = lastComment.value.trim();
                                                if (desp) map.desp = desp;
                                            }
                                        }

                                        if (isNotEmptyObject(map)) props[propName] = map;
                                    }
                                }
                            }

                            // ...
                        } else if (isArrayExpression(optionValue)) {
                            for (const item of optionValue.elements) {
                                if (isStringLiteral(item)) {
                                    const name = item.value;
                                    props[name] = {};
                                }
                            }
                        }

                        if (isNotEmptyObject(props)) parseResult.props = props;
                        break;
                    }

                    /**
                     * Methods 选项 - { [key: string]: Function }
                     *
                     * https://cn.vuejs.org/v2/api/#methods
                     */
                    case 'methods': {
                        const methods: IMethods = {};

                        if (isObjectExpression(optionValue)) {
                            for (const method of optionValue.properties) {
                                if (isObjectMethod(method)) {
                                    // ...
                                    const methodName = getMethodName(method);
                                    if (methodName && method.leadingComments) {
                                        // 解析外部可调用函数
                                        const methodInfo = parseCommentForPublicMark(method.leadingComments);
                                        if (methodInfo) {
                                            methods[methodName] = methodInfo;

                                            // 如果注释没有说明参数
                                            // 则从函数中读取参数
                                            if (!methods[methodName].params && method.params) {
                                                methods[methodName].params = [];
                                                for (const param of method.params) {
                                                    if (isIdentifier(param)) {
                                                        methods[methodName].params?.push({
                                                            name: param.name
                                                        });
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        if (isNotEmptyObject(methods)) parseResult.methods = methods;
                        break;
                    }

                    /**
                     * Model 选项 - { prop?: string, event?: string }
                     *
                     * https://cn.vuejs.org/v2/api/#model
                     */
                    case 'model': {
                        const model: IModel = parseResult.model;
                        if (isObjectExpression(optionValue)) {
                            for (const prop of optionValue.properties) {
                                if (isObjectProperty(prop)) {
                                    const propName = getObjectPropName(prop);
                                    const propValue = getObjectPropertyValue(prop, script.content);
                                    if (typeof propValue === 'string' && (propName === 'prop' || propName === 'event')) {
                                        model[propName] = { value: propValue };
                                    }
                                }
                            }
                        }
                        break;
                    }
                }
            }
        },

        // 解析语句注释
        ExpressionStatement(nodePath) {
            const statement = nodePath.node;

            // 解析 Event
            if (isEmitEvent(statement)) {
                const { event: eventName } = getEmitEventInfo(statement) || {};
                if (eventName) {
                    const events = parseResult.events || {};
                    combineEmitEventInfo(events, { eventName });

                    // 通过注解解析 Event
                    if (statement.leadingComments) {
                        const info = parseCommentForVueEvent(statement.leadingComments);
                        if (info) {
                            combineEmitEventInfo(events, { eventName, desp: info.desp, params: info.params });
                        }
                    }

                    if (!parseResult.events && isNotEmptyObject(events)) parseResult.events = events;
                }
            }
        }
    });

    // 解析全局注释
    if (scriptAst.comments) {
        for (const comment of scriptAst.comments) {
            // ...
            // 通过注解解析 Event
            // 全局注释 Event 会覆盖语句的注释 Event
            const events = parseResult.events || {};
            const infos = parseCommentForEventMark(comment);
            if (infos.length) {
                infos.forEach(info => {
                    const { name, desp, params } = info;
                    // 只有调用过 $emit 触发的事件才会被收集
                    if (name && name in events) {
                        combineEmitEventInfo(events, { eventName: name, desp, params });
                    }
                });
                if (!parseResult.events && isNotEmptyObject(events)) parseResult.events = events;
            }
        }
    }

    if (isNotEmptyObject(parseResult)) return parseResult;
}
